﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using NumericalData;
using NumericalData.Systems;
using NumericalData.SolvationMethods;
using NumericalData.SolvationMethods.Async;
using NumericalData.Statistics;
using WPFSolution.CommonOperations;
using Vector = NumericalData.Vector;

namespace WPFSolution.UserControls
{
    /// <summary>
    /// Interaction logic for LargeDimentionMatrixSolver.xaml
    /// </summary>
    public partial class LargeDimentionMatrixSolver : UserControl
    {

        private CancellationTokenSource _cancelSource;
        private int _dimention;
        private double _accur;
        private double _avg = 10.0;
        private SLAE _sys;

        private IAsyncSolvable<DiagnosticsAnswer> _solveMethod;

        #region Dependency Properties
        public static readonly DependencyProperty StatusStringProperty =
           DependencyProperty.Register("StatusString", typeof(string), typeof(LargeDimentionMatrixSolver), new PropertyMetadata(default(string)));
        /// <summary>
        /// Current Control Status
        /// </summary>
        public string StatusString
        {
            get { return (string)GetValue(StatusStringProperty); }
            set { SetValue(StatusStringProperty, value); }
        }

        public static readonly DependencyProperty IsBusyProperty =
            DependencyProperty.Register("IsBusy", typeof(bool), typeof(LargeDimentionMatrixSolver), new PropertyMetadata(default(bool)));
        /// <summary>
        /// Calculation Status
        /// </summary>
        public bool IsBusy
        {
            get { return (bool)GetValue(IsBusyProperty); }
            set { SetValue(IsBusyProperty, value); }
        }
        #endregion

        public LargeDimentionMatrixSolver()
        {
            InitializeComponent();
            ChangeStatus("");
        }

        #region Status
        void SetCalculationStatus(string status)
        {
            ChangeStatus(status);
            CalculationStatusTextBlock.Text = status;
        }

        private void ChangeStatus(string newStatus)
        {
            if (newStatus == "")
            {
                StatusString = "Large Matrix Solver Control Is Ready";
                IsBusy = false;
            }
            else
            {
                StatusString = newStatus;
                IsBusy = true;
            }
        }

        void ToggleEnablenessParametersPanel()
        {
            if (MatrixOptionsGroupBox != null)
            {
                if (!MatrixOptionsGroupBox.IsEnabled)
                {
                    MatrixOptionsGroupBox.IsEnabled = true;
                    ChangeStatus("");
                    SetCalculationStatus("Calculated!");
                    ProcessStatusGroupBox.Visibility = Visibility.Collapsed;
                    CancelButton.Visibility = Visibility.Collapsed;
                    SolveButton.Visibility = Visibility.Visible;
                }
                else
                {
                    MatrixOptionsGroupBox.IsEnabled = false;
                    ChangeStatus("Busy, Calculating");
                    ProcessStatusGroupBox.Visibility = Visibility.Visible;
                    CancelButton.Visibility = Visibility.Visible;
                    SolveButton.Visibility = Visibility.Collapsed;
                }
            }
        }
        #endregion

        int GetDimention()
        {
            return GUIManipulator.GetIntValue(DimentionsNumberTextBox, 1000, 10000, 1000);
        }

        int GetMaximumIterations()
        {
            return GUIManipulator.GetIntValue(maximumIterationsTextBox, 10000, 100000, 1000);
        }

        double GetAccuracy()
        {
            return GUIManipulator.GetDoubleValue(accuracyTextBox, 0.001, 0.01, 0.001);
        }

        private void AccuracyTextBox_LostFocus(object sender, RoutedEventArgs e)
        {
            if (accuracyTextBox != null)
            {
                accuracyTextBox.Text = GetAccuracy().ToString();
            }
        }

        private void MaximumIterationsTextBox_LostFocus(object sender, RoutedEventArgs e)
        {
            if (maximumIterationsTextBox != null)
            {
                maximumIterationsTextBox.Text = GetMaximumIterations().ToString();
            }
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            if (MethodsComboBox != null)
            {
                _dimention = GetDimention();
                _accur = GetAccuracy();
                switch (((ListBoxItem)(MethodsComboBox.SelectedItem)).Name)
                {
                    case ("YakobiMethodItem"):
                        _solveMethod = new YakobiImplicitMethod();
                        _cancelSource = MatrixGenerators<SquareMatrix>.AsyncMatrixGenerate(_dimention, _avg,
                                                                                           MatrixGenerators<SquareMatrix>.
                                                                                               GenerateMatrixWithDiagonalDominance,
                                                                                           MatrixGenerated, GotException,
                                                                                           Canceled);
                        break;
                    case ("SimpleMethodItem"):
                        _solveMethod = new SimpleExplicitSolvationMethod();
                        _cancelSource = MatrixGenerators<SquareMatrix>.AsyncMatrixGenerate(_dimention, _avg,
                                                                                           MatrixGenerators<SquareMatrix>.GeneratePositiveDefinedMatrix,
                                                                                           MatrixGenerated, GotException,
                                                                                           Canceled);
                        break;
                    case ("AdaptiveMethodItem"):
                        _solveMethod = new AdaptiveExplicitSolvationMethod();
                        _cancelSource = MatrixGenerators<SquareMatrix>.AsyncMatrixGenerate(_dimention, _avg,
                                                                   MatrixGenerators<SquareMatrix>.GeneratePositiveDefinedMatrix,
                                                                   MatrixGenerated, GotException,
                                                                   Canceled);
                        break;
                    default:
                        throw new Exception("Unknown Method!");
                }
                ToggleEnablenessParametersPanel();
                SetCalculationStatus("Generating matrix");
            }
        }

        private void MatrixGenerated(SquareMatrix matr)
        {
            SetCalculationStatus("Matrix generated\nGenerating Vector");
            var vec = Vector.Generate(_dimention, _avg);
            _sys = new SLAE(matr,vec);
            SetCalculationStatus("System generated\nCalculating Answer...");
            _cancelSource = _solveMethod.AsyncSolve(_sys, _accur,false,GetMaximumIterations(), GotAnswer, GotException, Canceled);
        }

        private void GotAnswer(DiagnosticsAnswer ans)
        {
            AnswerGroupBox.SetAnswer(ans,false);
            ToggleEnablenessParametersPanel();
        }

        private void GotException(Exception exc)
        {
            MessageBox.Show("Caught exception!", "An error occurred!",
                MessageBoxButton.OK, MessageBoxImage.Error);
            ToggleEnablenessParametersPanel();
        }

        private void Canceled(OperationCanceledException exc)
        {
            if (_cancelSource != null)
            {
                MessageBox.Show("Process Canceled!" + exc.Message, "Process Canceled!",
                    MessageBoxButton.OK, MessageBoxImage.Information);
                //Thread.CurrentThread.Abort();
                //exc.CancellationToken.ThrowIfCancellationRequested();
            }
        }

        private void CancelButton_Click(object sender, RoutedEventArgs e)
        {
            _cancelSource.Cancel();
            ToggleEnablenessParametersPanel();
        }


    }
}
